package cn.com.blueInfo.autoCode.vue;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 创建jsApi文件
 */
@Component
public class CreateScriptApi {

    @Autowired
    private RequestMappingHandlerMapping handlerMapping;

    public void handler() {
        List<ApiInfo> apiInfoList = scanControllers("cn.com.blueInfo.bpm");

        List<ApiInfo>
                numberList = new ArrayList<>(),
                personList = new ArrayList<>(),
                systemList = new ArrayList<>(),
                templateList = new ArrayList<>();

        for (ApiInfo apiInfo : apiInfoList) {
            String moduleName = apiInfo.getModuleName();
            switch (moduleName) {
                case "number":
                    numberList.add(apiInfo);
                    break;
                case "person":
                    personList.add(apiInfo);
                    break;
                case "system":
                    systemList.add(apiInfo);
                    break;
                case "template":
                    templateList.add(apiInfo);
                    break;
                default:
                    break;
            }
        }

        Map<String, List<ApiInfo>>
                numberGroup = listGroup(numberList),
                personGroup = listGroup(personList),
                systemGroup = listGroup(systemList),
                templateGroup = listGroup(templateList);

        List<Map<String, String>> numberFilesInfo = getFileInfo(numberGroup);
        List<Map<String, String>> personFilesInfo = getFileInfo(personGroup);
        List<Map<String, String>> systemFilesInfo = getFileInfo(systemGroup);
        List<Map<String, String>> templateFilesInfo = getFileInfo(templateGroup);
        for (Map<String, String> fileInfo : numberFilesInfo) writeFile(fileInfo);
        for (Map<String, String> fileInfo : personFilesInfo) writeFile(fileInfo);
        for (Map<String, String> fileInfo : systemFilesInfo) writeFile(fileInfo);
        for (Map<String, String> fileInfo : templateFilesInfo) writeFile(fileInfo);

    }

    private void writeFile(Map<String, String> fileInfo) {
        String moduleName = fileInfo.get("moduleName");
        String fileName = fileInfo.get("fileName");
//        fileName = fileName.substring(0, 1).toUpperCase() + fileName.substring(1);
        fileName = fileName.concat(File.separator).concat("api.js");
        String fileContent = fileInfo.get("fileContent");
        String filePath = "D:\\Development Program\\ProjectCode_idea\\LanxiPavilion\\LanxiPavilion-parent" +
                "\\lanxi-pavilion-vite-vue3\\src\\views";
        filePath = filePath.concat(File.separator).concat("bpm")
                .concat(File.separator).concat(moduleName);
        String apiFilePath = filePath.concat(File.separator).concat(fileName);
        String serviceFilePath = apiFilePath.replace("api.", "service.");
        try {
            FileUtils.writeStringToFile(
                    new File(apiFilePath),
                    fileContent,
                    StandardCharsets.UTF_8
            );
            FileUtils.writeStringToFile(
                    new File(serviceFilePath),
                    CreateScriptService.handler(),
                    StandardCharsets.UTF_8
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private List<ApiInfo> scanControllers(String basePackage) {
        List<ApiInfo> apiInfos = new ArrayList<>();

        Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            RequestMappingInfo requestMappingInfo = entry.getKey();
            HandlerMethod handlerMethod = entry.getValue();
            Class<?> controllerClass = handlerMethod.getBeanType();

            // 过滤出指定包下的Controller
            if (!controllerClass.getPackage().getName().startsWith(basePackage)) {
                continue;
            }

            // 解析模块名（从包路径提取，如"com.xxx.module.system" → "system"）
            String packageName = controllerClass.getPackage().getName();
            String moduleName = packageName.split("bpm\\.")[1].split("\\.")[0];

            // 解析Controller名称（如UserController → "user"，首字母小写）
            String controllerSimpleName = controllerClass.getSimpleName();
            String controllerName = controllerSimpleName.replace("Controller", "");
            controllerName = StringUtils.uncapitalize(controllerName);

            String dtoClass = packageName.substring(0, packageName.lastIndexOf("."));
            dtoClass = dtoClass.concat(".request.").concat(StringUtils.capitalize(controllerName)).concat("DTO");

            // 解析类级别基础路径
            RequestMapping classRequestMapping = AnnotationUtils.findAnnotation(controllerClass, RequestMapping.class);
            String basePath = classRequestMapping != null && classRequestMapping.value().length > 0
                    ? classRequestMapping.value()[0] : "";

            // 解析方法级别接口路径和请求方法
            PathPatternsRequestCondition pathCondition = requestMappingInfo.getPathPatternsCondition();
            if (requestMappingInfo.getMethodsCondition().getMethods().isEmpty()) {
                continue;
            }
            String methodPath = null; // 方法路径（如"/list"）
            if (pathCondition != null) {
                methodPath = pathCondition.getPatternValues().iterator().next();
            }
            String httpMethod = requestMappingInfo.getMethodsCondition().getMethods().iterator().next().name(); // GET/POST

            // 完整接口路径（拼接基础路径和方法路径，处理重复斜杠）
            String apiPath = null;
            if (methodPath != null) {
                apiPath = (methodPath).replaceAll("//", "/");
            }

            // 解析方法名和参数（简化处理，实际可解析@RequestParam/@PathVariable等）
            Method method = handlerMethod.getMethod();
            String methodName = method.getName();
            List<String> params = Arrays.stream(method.getParameterTypes())
                    .map(Class::getSimpleName)
                    .collect(Collectors.toList());

            // 1. 提取类上的 @Tag(name = "...")
            Tag classTag = AnnotationUtils.findAnnotation(controllerClass, Tag.class);
            String tagName = classTag != null ? classTag.name() : "";

            // 2. 提取方法上的 @Operation(summary = "...")
            Operation methodOperation = AnnotationUtils.findAnnotation(method, Operation.class);
            String operationSummary = methodOperation != null ? methodOperation.summary() : "";

            // 封装接口信息
            ApiInfo apiInfo = new ApiInfo();
            apiInfo.setModuleName(moduleName);
            apiInfo.setControllerName(controllerName);
            apiInfo.setBasePath(basePath);
            apiInfo.setApiPath(apiPath);
            apiInfo.setHttpMethod(httpMethod);
            apiInfo.setMethodName(methodName);
            apiInfo.setParams(params);
            apiInfo.setControllerComment(tagName);
            apiInfo.setMethodComment(operationSummary);
            apiInfo.setDtoClass(dtoClass);
            apiInfos.add(apiInfo);
        }
        return apiInfos;
    }

    private Map<String, List<ApiInfo>> listGroup(List<ApiInfo> list) {
        Map<String, List<ApiInfo>> group = new HashMap<>();
        for (ApiInfo number : list) {
            String controllerName = number.getControllerName();
            if (group.containsKey(controllerName)) {
                List<ApiInfo> currentList = group.get(controllerName);
                currentList.add(number);
            } else {
                List<ApiInfo> list1 = new ArrayList<>();
                list1.add(number);
                group.put(number.getControllerName(), list1);
            }
        }
        return group;
    }

    // 接口信息实体类
    @Data
    private static class ApiInfo {
        private String moduleName; // 模块名（如system）
        private String controllerName; // Controller名（如UserController）
        private String basePath; // 类级别@RequestMapping路径
        private String methodName; // 方法名（如list）
        private String apiPath; // 完整接口路径（basePath + 方法路径）
        private String httpMethod; // 请求方法（GET/POST等）
        private List<String> params; // 参数列表（简化处理）
        private String controllerComment; // Controller类的文档注释
        private String methodComment;     // 接口方法的文档注释
        // 新增：存储方法参数的Class对象（用于提取DTO信息）
        private String dtoClass;
    }

    @Data
    private static class ApiParam {
        private String requestUrl;

        private String createComment;
        private String createMethod;
        private String createParam;
        private String createUrl;

        private String removeComment;
        private String removeMethod;
        private String removeParam;
        private String removeUrl;

        private String updateFullComment;
        private String updateFullMethod;
        private String updateFullParam1;
        private String updateFullParam2;
        private String updateFullUrl;

        private String updatePartialComment;
        private String updatePartialMethod;
        private String updatePartialParam1;
        private String updatePartialParam2;
        private String updatePartialUrl;

        private String getComment;
        private String getMethod;
        private String getParam;
        private String getUrl;

        private String pageComment;
        private String pageMethod;
        private String pageUrl;

        private String listComment;
        private String listMethod;
        private String listUrl;

        private DtoInfo dtoInfo;

    }

    private List<Map<String, String>> getFileInfo(Map<String, List<ApiInfo>> groupMap) {
        List<Map<String, String>> resultList = new ArrayList<>();
        for (Map.Entry<String, List<ApiInfo>> entry : groupMap.entrySet()) {
            Map<String, String> result = new HashMap<>();
            String fileName = entry.getKey();
            result.put("fileName", fileName);
            List<ApiInfo> apiInfoList1 = entry.getValue();
            String moduleName = "";
            ApiParam apiParam = new ApiParam();
            for (ApiInfo apiInfo : apiInfoList1) {
                String requestUrl = apiInfo.getBasePath();
                apiParam.setRequestUrl(requestUrl);
                String httpMethod = apiInfo.getHttpMethod();

                moduleName = apiInfo.getModuleName();

                String comment = apiInfo.getMethodComment();
                String method = apiInfo.getMethodName();
                List<String> params = apiInfo.getParams();
                String param = "", param1 = "", param2 = "";
                if (params.size() == 1) {
                    param = params.get(0);
                    param = "Long".equals(param) ? "id" : StringUtils.uncapitalize(param);
                    param = param.indexOf("DTO") > 0 ? param.substring(0, param.indexOf("DTO")) : param;
                } else {
                    for (String oneParam : params) {
                        if ("Long".equals(oneParam)) {
                            param1 = "id";
                        } else {
                            param2 = StringUtils.uncapitalize(oneParam);
                            param2 = param2.indexOf("DTO") > 0 ? param2.substring(0, param2.indexOf("DTO")) : param2;
                        }
                    }
                }
                String url = apiInfo.getApiPath();
                url = url.substring(requestUrl.length() + 1);
                url = url.lastIndexOf("/") > 0 ? url.substring(0, url.lastIndexOf("/")) : url;

                DtoInfo currDtoInfo = getSpecifiedDtoInfo(apiInfo.getDtoClass());

                switch (httpMethod) {
                    case "POST":
                        apiParam.setCreateComment(comment);
                        apiParam.setCreateMethod(method);
                        apiParam.setCreateParam(param);
                        apiParam.setCreateUrl(url);
                        break;
                    case "DELETE":
                        apiParam.setRemoveComment(comment);
                        apiParam.setRemoveMethod(method);
                        apiParam.setRemoveParam(param);
                        apiParam.setRemoveUrl(url);
                        break;
                    case "PUT":
                        apiParam.setUpdateFullComment(comment);
                        apiParam.setUpdateFullMethod(method);
                        apiParam.setUpdateFullParam1(param1);
                        apiParam.setUpdateFullParam2(param2);
                        apiParam.setUpdateFullUrl(url);
                        break;
                    case "PATCH":
                        apiParam.setUpdatePartialComment(comment);
                        apiParam.setUpdatePartialMethod(method);
                        apiParam.setUpdatePartialParam1(param1);
                        apiParam.setUpdatePartialParam2(param2);
                        apiParam.setUpdatePartialUrl(url);
                        break;
                    case "GET":
                        if (url.contains("Infos")) {
                            apiParam.setPageComment(comment);
                            apiParam.setPageMethod(method);
                            apiParam.setPageUrl(url);
                            apiParam.setDtoInfo(currDtoInfo);
                        } else if (url.contains("InfoList")) {
                            apiParam.setListComment(comment);
                            apiParam.setListMethod(method);
                            apiParam.setListUrl(url);
                        } else {
                            apiParam.setGetComment(comment);
                            apiParam.setGetMethod(method);
                            apiParam.setGetParam(param);
                            apiParam.setGetUrl(url);
                        }
                        break;
                    default:
                        break;
                }
            }
            result.put("fileContent", getJsFileContent(apiParam));
            result.put("moduleName", moduleName);
            resultList.add(result);
        }
        return resultList;
    }

    private String getJsFileContent(ApiParam apiParam) {
        return "import {axiosClient} from \"@/utils/axiosClient.js\";\n" +
                "import {handleCommonParams} from \"@/api/commonParams.js\";\n" +
                "\n" +
                "const requestUrl = '" + apiParam.getRequestUrl() + "'\n" +
                "\n" +
                "const api = {\n" +
                "\n" +
                "    /**\n" +
                "     * " + apiParam.getCreateComment() + "\n" +
                "     * @param " + apiParam.getCreateParam() + "\n" +
                "     * @returns {Promise<*|undefined>}\n" +
                "     */\n" +
                "    " + apiParam.getCreateMethod() + ": (" + apiParam.getCreateParam() + ") =>\n" +
                "        axiosClient().post(`${requestUrl}/" + apiParam.getCreateUrl() + "`, " + apiParam.getCreateParam() + "),\n" +
                "\n" +
                "    /**\n" +
                "     * " + apiParam.getRemoveComment() + "\n" +
                "     * @param " + apiParam.getRemoveParam() + "\n" +
                "     * @returns {Promise<*|undefined>}\n" +
                "     */\n" +
                "    " + apiParam.getRemoveMethod() + ": (" + apiParam.getRemoveParam() + ") =>\n" +
                "        axiosClient().remove(`${requestUrl}/" + apiParam.getRemoveUrl() + "/${" + apiParam.getRemoveParam() +
                "}`)," +
                "\n" +
                "\n" +
                "    /**\n" +
                "     * " + apiParam.getUpdateFullComment() + "\n" +
                "     * @param " + apiParam.getUpdateFullParam1() + "\n" +
                "     * @param " + apiParam.getUpdateFullParam2() + "\n" +
                "     * @returns {Promise<*|undefined>}\n" +
                "     */\n" +
                "    " + apiParam.getUpdateFullMethod() + ": (" + apiParam.getUpdateFullParam1() + ", " +
                apiParam.getUpdateFullParam2() + ") =>\n" +
                "        axiosClient().put(`${requestUrl}/" + apiParam.getUpdateFullUrl() + "/${" + apiParam.getUpdateFullParam1() +
                "}`, " + apiParam.getUpdateFullParam2() + "),\n" +
                "\n" +
                "    /**\n" +
                "     * " + apiParam.getUpdatePartialComment() + "\n" +
                "     * @param " + apiParam.getUpdatePartialParam1() + "\n" +
                "     * @param " + apiParam.getUpdatePartialParam2() + "\n" +
                "     * @returns {Promise<*|undefined>}\n" +
                "     */\n" +
                "    " + apiParam.getUpdatePartialMethod() + ": (" + apiParam.getUpdatePartialParam1() + "," +
                " " + apiParam.getUpdatePartialParam2() + ") " +
                "=>\n" +
                "        axiosClient().patch(`${requestUrl}/" + apiParam.getUpdatePartialUrl() + "/${" + apiParam.getUpdatePartialParam1() +
                "}`, " + apiParam.getUpdatePartialParam2() +
                "),\n" +
                "\n" +
                "    /**\n" +
                "     * " + apiParam.getGetComment() + "\n" +
                "     * @param " + apiParam.getGetParam() + "\n" +
                "     * @returns {Promise<*|undefined>}\n" +
                "     */\n" +
                "    " + apiParam.getGetMethod() + ": (" + apiParam.getGetParam() + ") =>\n" +
                "        axiosClient().get(`${requestUrl}/" + apiParam.getGetUrl() + "/${" + apiParam.getGetParam() + "}`),\n" +
                "\n" +
                "    /**\n" +
                "     * " + apiParam.getPageComment() + "\n" +
                "     * @param page\n" +
                "     * @param size\n" +
                "     * @param businessParam\n" +
                "     * @returns {Promise<axios.AxiosResponse<any>|undefined>}\n" +
                "     */\n" +
                "    " + apiParam.getPageMethod() + ": (page, size, businessParam) => {\n" +
                "        const params = new URLSearchParams();\n" +
                "        params.append('page', page)\n" +
                "        params.append('size', size)\n" +
                "        setBusinessParams(params, businessParam)\n" +
                "        return axiosClient().get(`${requestUrl}/" + apiParam.getPageUrl() + "?${params.toString()}`)\n" +
                "    },\n" +
                "\n" +
                "    /**\n" +
                "     * " + apiParam.getListComment() + "\n" +
                "     * @param businessParam\n" +
                "     * @returns {Promise<axios.AxiosResponse<any>|undefined>}\n" +
                "     */\n" +
                "    " + apiParam.getListMethod() + ": (businessParam) => {\n" +
                "        const params = new URLSearchParams();\n" +
                "        setBusinessParams(params, businessParam)\n" +
                "        return axiosClient().get(`${requestUrl}/" + apiParam.getListUrl() + "?${params.toString()}`)\n" +
                "    }\n" +
                "\n" +
                "}\n" +
                "\n" +
                "const setBusinessParams = (params, businessParam) => {\n" +
                getPageInfosParam(apiParam.getDtoInfo()) +
                "}\n" +
                "\n" +
                "export default api\n";
    }

    private String getPageInfosParam(DtoInfo dtoInfo) {
        List<String> fieldList = dtoInfo.getFields();

        String paramStr = "    if (businessParam) {\n";

        for (String field : fieldList) {
            paramStr = paramStr.concat(
                    "        if (businessParam." + field + ") {\n" +
                            "            params.append('" + field + "', businessParam." + field + ")\n" +
                            "        }\n");
        }
        paramStr = paramStr.concat(getCommonParam());

        paramStr = paramStr.concat("    }\n");
        return paramStr;
    }

    private String getCommonParam() {
        return "        handleCommonParams(params, businessParam);\n";
    }

    // 新增内部类：封装DTO的详细信息（保持不变）
    @Data
    private static class DtoInfo {
        private String className; // DTO类名（如UserDTO）
        private String fullClassName; // 全限定名（如cn.com.blueInfo.bpm.dto.UserDTO）
        private List<String> fields; // 属性名列表（如["id", "name"]）
        private List<String> fieldTypes; // 属性类型列表（如["Long", "String"]）
        private List<String> fieldComments; // 属性注释（若有）
    }

    // 修改后的方法：通过DTO全限定类路径直接反射获取属性
    private DtoInfo getSpecifiedDtoInfo(String dtoClassPath) {
        if (StringUtils.isBlank(dtoClassPath)) {
            throw new IllegalArgumentException("DTO全限定类路径不能为空");
        }

        try {
            // 加载目标DTO类
            Class<?> dtoClass = Class.forName(dtoClassPath);

            DtoInfo dtoInfo = new DtoInfo();
            dtoInfo.setClassName(dtoClass.getSimpleName());
            dtoInfo.setFullClassName(dtoClass.getName());

            // 获取所有属性（包括私有属性）
            List<String> fields = new ArrayList<>();
            List<String> fieldTypes = new ArrayList<>();
            List<String> fieldComments = new ArrayList<>();

            // 遍历类的所有字段（含父类公共字段，若需要可扩展为getDeclaredFields() + 父类递归）
            for (Field field : dtoClass.getDeclaredFields()) {
                // 跳过静态字段和合成字段（如lambda生成的）
                if (java.lang.reflect.Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
                    continue;
                }

                fields.add(field.getName());
                fieldTypes.add(field.getType().getSimpleName());

                // 可选：提取字段注释（若使用了Swagger注解如@ApiModelProperty）
                io.swagger.v3.oas.annotations.media.Schema schema = AnnotationUtils.findAnnotation(field, io.swagger.v3.oas.annotations.media.Schema.class);
                fieldComments.add(schema != null ? schema.description() : "");
            }

            dtoInfo.setFields(fields);
            dtoInfo.setFieldTypes(fieldTypes);
            dtoInfo.setFieldComments(fieldComments);

            return dtoInfo;

        } catch (ClassNotFoundException e) {
            throw new RuntimeException("未找到DTO类：" + dtoClassPath, e);
        } catch (Exception e) {
            throw new RuntimeException("获取DTO信息失败：" + e.getMessage(), e);
        }
    }

}
